<template>
	<div class="container">
		<div class="chat-box">
			<div class="left">
				<div class="head">
					<div class="title-left">聊天机器人</div>
					<div class="retract-right" @click="state.isRetract = !state.isRetract">
						<SvgIcon v-if="state.isRetract" name="ele-Fold" :size="22" color="var(--el-color-primary)" />
						<SvgIcon v-else name="ele-Expand" :size="22" color="var(--el-color-primary)" />
					</div>
				</div>
				<div class="dialog-box">
					<div class="chattingRecord">
						<div class="not-chat">已展示全部消息~</div>
						<template v-for="(chatItem, index) in state.chattingRecordList" :key="'chat-item' + index">
							<div v-if="chatItem.identity === EIdentity.StartCommunication" class="communication-time-box">
								<div class="communication-time">{{ chatItem.time }} 开始沟通</div>
							</div>
							<div v-else class="chat-item" :class="{ customer: chatItem.identity === EIdentity.Customer }">
								<!-- <div class="title">我 今天 15:51:33</div> -->
								<div class="title">{{ chatItem.name }} {{ formatPastComputed(chatItem.time) }}</div>
								<div class="content" v-if="[EMsgType.text, EMsgType.img].includes(chatItem.ContentType)">
									<span v-if="chatItem.ContentType === EMsgType.text">{{ chatItem.content }}</span>
									<el-image
										v-else-if="chatItem.ContentType === EMsgType.img"
										style="width: 100px; height: 100px"
										:src="chatItem.content"
										:initial-index="1"
										fit="fill"
										:preview-src-list="[chatItem.content!]"
										lazy
									/>
								</div>
								<div
									v-else-if="chatItem.ContentType === EMsgType.file"
									class="file"
									@dblclick="handleFileDown(chatItem.content.title, chatItem.content.link)"
								>
									<el-image style="width: 40px; height: 40px" :src="chatItem.content.icon || defaultImgIcon" fit="fill" lazy />
									<div class="info">
										<div class="title">{{ chatItem.content.title }}</div>
										<span class="size">{{ chatItem.content.size }}</span>
										<span class="status">{{ chatItem.content.status }}</span>
									</div>
								</div>
							</div>
						</template>
					</div>
				</div>
				<div class="handler">
					<div class="emoji-content" v-click-outside="onClickOutside" :style="[isShowEmoji ? 'display: block;' : '']">
						<div v-for="emojiKey in Object.keys(emojiObj)" :key="emojiKey" class="emoji" @click="handlerClickEmoji(emojiKey as keyof emojiKeyType)">
							{{ emojiObj[emojiKey] }}
						</div>
					</div>
					<div class="input" ref="inputRef" @input="getContent" contenteditable="true">{{ state.content }}</div>
					<div class="tool-bar">
						<div class="tool">
							<img src="../../../assets/icon/image.svg" alt="" @click="handlerImgUpload" />
							<img src="../../../assets/icon/biaoq.svg" alt="" @click="handlerShowEmoji" />
							<img src="../../../assets/icon/file.svg" alt="" @click="handlerFileUpload" />
						</div>
						<div class="submit">
							<el-button
								type="primary"
								size="default"
								@click="
									() => {
										state.msgTypes = EMsgType.text;
										sendMessageReq();
									}
								"
								>发送</el-button
							>
						</div>
					</div>
				</div>
			</div>
			<div class="right" :style="[!state.isRetract ? 'width:0px;' : '']">
				<template v-if="state.isRetract">
					<img class="avatar" src="../../../assets/icon/default-avatar.svg" alt="" />
					<div class="title">客服机器人</div>
					<div class="address"><img src="../../../assets/icon/location.svg" alt="" /><span>江苏省南京市江宁区</span></div>
					<div class="domain-name"><img src="../../../assets/icon/domain-name.svg" alt="" /><span>www.baidu.com</span></div>
				</template>
			</div>

			<Upload
				style="display: none"
				accept=".jpg,.png"
				input-type="7"
				ref="uploadRef"
				@on-change-file-list="(file) => handlerChangeFileList(file, 'uploadRef')"
			>
				<button ref="uploadBtnRef">上传</button>
			</Upload>
			<Upload style="display: none" input-type="7" ref="uploadFileRef" @on-change-file-list="(file) => handlerChangeFileList(file, 'uploadFileRef')">
				<button ref="uploadFileBtnRef">上传</button>
			</Upload>
		</div>
	</div>
</template>
<script lang="ts" setup>
import { ws } from '/@/utils/websocket';
import { Fold } from '@element-plus/icons-vue';
import { ClickOutside as vClickOutside, ElMessage } from 'element-plus';
import Upload from '/@/components/Linxyun/FileClient/index.vue';
import { Base64 } from '/@/utils/b64';
import { httpReqeustApi } from '/@/api/linxyun/base/index.ts';
import { formatDate, formatPast } from '/@/utils/formatTime';
import { sizeTostr, handleFileDown } from '/@/utils/extendMethods';

import textImgIcon from '/@/assets/icon/file-icon-text.png';
import defaultImgIcon from '/@/assets/icon/file-icon-default.png';
import imgIcon from '/@/assets/icon/file-icon-img.png';
enum EMsgType {
	img = '1', // 图片
	text = '0', // 文本
	file = '2', // 文件
}
enum EIdentity {
	CustomerService, // 客服
	Customer, // 客户
	StartCommunication, // 开始交流时间
}

interface IChattingRecord {
	name: string;
	time: Date;
	content: any;
	identity: EIdentity;
	ContentType: EMsgType;
}
interface IState {
	serveType: number | null;
	Recver: string;
	chatUserID: string; // 用户ID
	msgTypes: EMsgType;
	uploadUrl: string;
	content: string;
	fileUrlPrefix: string;
	chattingRecordList: IChattingRecord[];
	self: Record<string, any>;
	isRetract: boolean;
	fileIcon: string;
	fileInfo: Record<string, any>;
}

const httpRequest = httpReqeustApi();
const emojiObj = reactive({
	grinning: '😀',
	grin: '😁',
	joy: '😂',
	smiley: '😃',
	smile: '😄',
	sweat_smile: '😅',
	laughing: '😆',
	satisfied: '😆',
	innocent: '😇',
	smiling_imp: '😈',
	wink: '😉',
	blush: '😊',
	yum: '😋',
	relieved: '😌',
	heart_eyes: '😍',
	sunglasses: '😎',
	smirk: '😏',
	neutral_face: '😐',
	expressionless: '😑',
	unamused: '😒',
	sweat: '😓',
	pensive: '😔',
	confused: '😕',
	confounded: '😖',
	kissing: '😗',
	kissing_heart: '😘',
	kissing_smiling_eyes: '😙',
	kissing_closed_eyes: '😚',
	stuck_out_tongue: '😛',
	stuck_out_tongue_winking_eye: '😜',
	stuck_out_tongue_closed_eyes: '😝',
	disappointed: '😞',
	worried: '😟',
	angry: '😠',
	rage: '😡',
	cry: '😢',
	persevere: '😣',
	triumph: '😤',
	disappointed_relieved: '😥',
	frowning: '😦',
	anguished: '😧',
	fearful: '😨',
	weary: '😩',
	sleepy: '😪',
	tired_face: '😫',
	grimacing: '😬',
	sob: '😭',
	open_mouth: '😮',
	hushed: '😯',
	cold_sweat: '😰',
	scream: '😱',
	astonished: '😲',
	flushed: '😳',
	sleeping: '😴',
	dizzy_face: '😵',
	no_mouth: '😶',
	mask: '😷',
	smile_cat: '😸',
	joy_cat: '😹',
	smiley_cat: '😺',
	heart_eyes_cat: '😻',
	smirk_cat: '😼',
	kissing_cat: '😽',
	pouting_cat: '😾',
	crying_cat_face: '😿',
	scream_cat: '🙀',
	slightly_frowning_face: '🙁',
	slightly_smiling_face: '🙂',
	upside_down_face: '🙃',
	face_with_rolling_eyes: '🙄',
	'woman-gesturing-no': '🙅‍♀️',
	no_good: '🙅‍♀️',
	'man-gesturing-no': '🙅‍♂️',
	'woman-gesturing-ok': '🙆‍♀️',
	ok_woman: '🙆‍♀️',
	'man-gesturing-ok': '🙆‍♂️',
	'woman-bowing': '🙇‍♀️',
	'man-bowing': '🙇‍♂️',
	bow: '🙇‍♂️',
	see_no_evil: '🙈',
	hear_no_evil: '🙉',
	speak_no_evil: '🙊',
	'woman-raising-hand': '🙋‍♀️',
	raising_hand: '🙋‍♀️',
	'man-raising-hand': '🙋‍♂️',
	raised_hands: '🙌',
	'woman-frowning': '🙍‍♀️',
	person_frowning: '🙍‍♀️',
	'man-frowning': '🙍‍♂️',
	'woman-pouting': '🙎‍♀️',
	person_with_pouting_face: '🙎‍♀️',
	'man-pouting': '🙎‍♂️',
	pray: '🙏',
});
type emojiKeyType = typeof emojiObj;

const state = reactive<IState>({
	serveType: null,
	Recver: '',
	chatUserID: '',
	msgTypes: EMsgType.text,
	uploadUrl: '',
	content: '',
	fileUrlPrefix: '',
	chattingRecordList: [], // 聊天记录
	self: {},
	isRetract: true,
	fileIcon: defaultImgIcon,
	fileInfo: {},
});
const { self } = toRefs(state);
const inputRef = ref();
const getContent = () => {
	state.content = inputRef.value.innerHTML || '';
	// console.log('state.content', state.content);
};

const isShowEmoji = ref(false);
const handlerShowEmoji = () => {
	isShowEmoji.value = !isShowEmoji.value;
};
const onClickOutside = () => {
	isShowEmoji.value = false;
};

const handlerClickEmoji = (emojiKey: keyof emojiKeyType) => {
	state.content += emojiObj[emojiKey];
};

const uploadRef = ref<InstanceType<typeof Upload>>();
const uploadBtnRef = ref();
// 上传图片
const handlerImgUpload = () => {
	uploadBtnRef.value?.click();
	state.msgTypes = EMsgType.img;
};
// 上传文件

const uploadFileRef = ref<InstanceType<typeof Upload>>();
const uploadFileBtnRef = ref();
const handlerFileUpload = () => {
	uploadFileBtnRef.value?.click();
	state.msgTypes = EMsgType.file;
};
const handlerChangeFileList = async (file: Record<string, any>, _uploadRef: 'uploadRef' | 'uploadFileRef') => {
	const resImg: { name: string; url: string }[] | undefined =
		_uploadRef === 'uploadRef' ? await uploadRef.value?.uploadSubmit() : await uploadFileRef.value?.uploadSubmit();
	console.log('handlerChangeFileList', resImg);
	if (resImg && resImg?.length === 1) {
		state.uploadUrl = state.fileUrlPrefix + resImg[0].url;
		state.fileInfo = file;
		sendMessageReq();
	} else {
		ElMessage.error('上传异常');
		// 本地保存聊天记录
		if (state.msgTypes === EMsgType.file) {
			const chattingRecord: IChattingRecord = {
				name: '我',
				time: formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS'),
				content: {
					link: state.uploadUrl,
					title: state.fileInfo.name,
					size: sizeTostr(state.fileInfo.size),
					status: '上传失败',
				},
				identity: EIdentity.Customer,
				ContentType: EMsgType.file,
			};
			saveChattingRecord(chattingRecord);
		}
	}
	_uploadRef === 'uploadRef' ? uploadRef.value?.fileListInnerClear() : uploadFileRef.value?.fileListInnerClear();
};
// 发送消息
const sendMessageReq = () => {
	console.log('sendMessageReq', state.msgTypes);
	if ((state.msgTypes === EMsgType.text && !state.content) || (state.msgTypes !== EMsgType.text && !state.uploadUrl)) {
		return;
	}
	let content: any = '';
	console.log('sendMessageReq', state.content);
	const params: Record<string, any> = {};
	params.Recver = state.Recver;
	params.Sender = state.chatUserID;
	params.optype = 'req';
	params.to_key = BASE_API.ENT_CODE + '#000';
	params.to_subkey = state.Recver;
	// value == 1 发送图片消息
	if (state.msgTypes === EMsgType.img) {
		params.ContentType = EMsgType.img;
		params.Message = state.uploadUrl;
		content = state.uploadUrl;
	} else if (state.msgTypes === EMsgType.text) {
		// 发送普通文字消息
		params.Message = Base64.encode(state.content);
		params.ContentType = EMsgType.text;
		content = state.content;
	} else if (state.msgTypes === EMsgType.file) {
		// 发送文件
		params.ContentType = EMsgType.file;
		params.Message = {
			link: state.uploadUrl,
			title: state.fileInfo.name,
			size: sizeTostr(state.fileInfo.size),
			status: '已上传',
		};
		const arr = state.uploadUrl.split('.');
		if (arr.length) {
			if (arr[arr.length - 1] === 'txt') {
				state.fileIcon = textImgIcon;
			} else if (['png', 'jpg'].includes(arr[arr.length - 1])) {
				state.fileIcon = imgIcon;
			} else {
				state.fileIcon = defaultImgIcon;
			}
		}
		params.Message.icon = state.fileIcon;
		content = params.Message;
	}

	// httpRequest.Post('sendIMReq', {}, params).then((res) => {
	// 	console.log('sendIMReq', res);
	// });
	ws.send(params);

	// 本地保存聊天记录
	const chattingRecord: IChattingRecord = {
		name: '我',
		time: formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS'),
		content: content,
		identity: EIdentity.Customer,
		ContentType: params.ContentType,
	};
	saveChattingRecord(chattingRecord);
	state.content = '';
	state.uploadUrl = '';
	nextTick(() => {
		scrollToBottom();
	});
	return;
};

// 随机ID
function setSessionId(len: number = 32) {
	const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
	const maxPos = chars.length;
	let pwd = '';
	for (var i = 0; i < len; i++) {
		pwd += chars.charAt(Math.floor(Math.random() * maxPos));
	}
	return pwd;
}

// 保存聊天记录
const saveChattingRecord = (chattingRecord: IChattingRecord) => {
	state.chattingRecordList.push(chattingRecord);
	const prefix = BASE_API.PROJECT_URI.slice(1);
	localStorage.setItem(prefix + '_chattingRecordList', JSON.stringify(state.chattingRecordList));
};

const formatPastComputed = computed(() => {
	return (date: Date) => {
		return formatPast(new Date(date));
	};
});
// 初始化
(() => {
	// 如果本地有UserID就直接取,没有就生成UserID
	if (!localStorage.getItem('chatUserID')) {
		const chatUserID = setSessionId();
		state.chatUserID = BASE_API.ENT_CODE + '-' + chatUserID;
		localStorage.setItem('chatUserID', BASE_API.ENT_CODE + '-' + chatUserID);
	} else {
		state.chatUserID = localStorage.getItem('chatUserID')!;
	}
	state.Recver = sessionStorage.getItem('Recver') || '';
	ws.connect(state.chatUserID);
	ws.onOpen(state.chatUserID);
	ws.recvData((onMessage) => {
		const res: Record<string, any> = JSON.parse(onMessage.data);
		console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息', res);
		if (res.Sender && res.Recver) {
			// 为空时保持接受者ID
			if (!state.Recver) {
				state.Recver = res.Sender;
				sessionStorage.setItem('Recver', res.Sender);
			}

			let content = '';
			// 本地保存聊天记录
			if (res.Message && res.ContentType === EMsgType.text) {
				content = Base64.decode(res.Message) || '';
			} else if (res.Message && [EMsgType.img, EMsgType.file].includes(res.ContentType)) {
				content = res.Message;
			}
			const chattingRecord: IChattingRecord = {
				name: '客服',
				time: formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS'),
				content: content,
				identity: EIdentity.CustomerService,
				ContentType: res.ContentType,
			};
			saveChattingRecord(chattingRecord);
		}
	});
	// 聊天记录内容
	const prefix = BASE_API.PROJECT_URI.slice(1);
	const localChatContentStr = localStorage.getItem(prefix + '_chattingRecordList');
	// 初始化历史数据
	if (localChatContentStr) {
		const localChatContent = JSON.parse(localChatContentStr);
		state.chattingRecordList = localChatContent || [];
	}
	// 本地保存聊天记录
	const chattingRecord: IChattingRecord = {
		name: '',
		time: formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS'),
		content: '',
		identity: EIdentity.StartCommunication,
		ContentType: EMsgType.text,
	};
	saveChattingRecord(chattingRecord);
	// 获取文件Url
	if (localStorage.getItem('setting')) {
		state.fileUrlPrefix = JSON.parse(localStorage.getItem('setting')!).ImagUrl;
	}
})();

onMounted(() => {
	nextTick(() => {
		scrollToBottom();
	});
});
const scrollToBottom = () => {
	const chattingRecordEl = document.querySelector('.dialog-box');
	if (chattingRecordEl) {
		chattingRecordEl.scrollTop = chattingRecordEl.scrollHeight;
	}
};
</script>
<style lang="scss" scoped>
.container {
	display: flex;
	justify-content: center;
	align-items: center;
	height: calc(100vh - 120px);
}
.chat-box {
	width: 800px;
	display: flex;
	background-color: var(--next-color-white);
	box-shadow: var(--el-box-shadow-light);
	.left {
		min-width: 560px;
		flex: 1;
		border-right: 1px solid #e6e8eb;
		.head {
			display: flex;
			justify-content: space-between;
			padding: 0px 10px;
			height: 44px;
			align-items: center;
			border-bottom: 1px solid #e6e8eb;
		}
		.dialog-box {
			height: 404px;
		}
	}
	.right {
		display: flex;
		flex-direction: column;
		align-items: center;
		padding-top: 40px;
		width: 240px;
		transition: width 0.5s;
		img {
			width: 18px;
			margin-right: 10px;
		}
		.avatar {
			width: 70px;
		}
		.title {
			font-size: 18px;
			margin-bottom: 25px;
		}
		.address,
		.domain-name {
			display: flex;
			width: 100%;
			margin-left: 20px;
			margin-bottom: 15px;
		}
	}
	.handler {
		border-top: 1px solid #e6e8eb;
		padding: 15px 15px 8px 15px;
		position: relative;
		.emoji-content {
			position: absolute;
			display: none;
			left: 10px;
			top: -190px;
			width: 360px;
			height: 180px;
			background-color: var(--next-color-white);
			overflow-y: auto;
			font-size: 20px;
			box-shadow: var(--el-box-shadow-light);

			.emoji {
				display: inline-flex;
				justify-content: center;
				width: 34px;
				height: 34px;
				cursor: pointer;
			}
		}
		.input {
			height: 50px;

			overflow-y: auto;
		}
		.tool-bar {
			display: flex;
			justify-content: space-between;
			.tool {
				display: flex;
				align-items: flex-end;
				img {
					width: 22px;
					cursor: pointer;
				}
				img:nth-child(2) {
					margin: 0 10px;
				}
			}
		}
	}
}
.dialog-box {
	width: 100%;
	background-color: var(--next-color-white);
	padding: 20px 15px;
	overflow-y: auto;
}
.not-chat {
	text-align: center;
	color: #909399;
	font-size: 12px;
}
.communication-time-box {
	text-align: center;
}
.communication-time {
	display: inline-block;
	margin: 24px 12px 0;
	padding: 5px 12px;
	font-size: 12px;
	line-height: 18px;
	color: #606266;
	border-radius: 4px;
	background: #f5f7fa;
}
.chat-item {
	margin-bottom: 16px;
	.title {
		margin-bottom: 4px;
		font-size: 12px;
		line-height: 12px;
		color: #a4a0aa;
		min-width: 66px;
	}
	.content {
		font-size: 14px;
		word-break: break-all;
		border-radius: 4px;
		border-radius: 0 8px 8px 8px;
		background: #eff1f7;
		white-space: pre-line;
		line-height: 22px;
		padding: 12px;
		max-width: 80%;
		display: inline-block;
	}
	.file {
		display: flex;
		border: 1px solid #ebeef5;
		width: 298px;
		height: 65px;
		background: #fff;
		border-radius: 8px 0 8px 8px;
		padding: 12px 8px 12px 12px;
		cursor: pointer;
		.el-image {
			// margin: 12px 8px 12px 12px;
			margin-right: 10px;
		}
		.info {
			width: calc(100% - 55px);
			.title {
				color: #303133;
				text-align: left;
				overflow: hidden;
				white-space: nowrap;
				text-overflow: ellipsis;
				width: 100%;
				margin-top: 3px;
				margin-bottom: 8px;
			}
			span {
				color: #909399;
				font-weight: 400;
				&.size {
					margin-right: 10px;
				}
			}
		}
	}
}
.customer {
	display: flex;
	flex-direction: column;
	align-items: flex-end;

	.content {
		border-radius: 8px 0px 8px 8px;
	}
}
.retract-right {
	cursor: pointer;
}
</style>
